package cc.ok200.api.websocket;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.stream.ChunkedWriteHandler;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.handler.timeout.IdleStateHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;

import java.net.InetSocketAddress;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

/**
 * WebSocket 服务器，基于 Netty 实现
 */
@Component
public class WebSocketServer {
    private static final Logger logger = LoggerFactory.getLogger(WebSocketServer.class);
    
    private static EventLoopGroup bossGroup = new NioEventLoopGroup(1);
    private static EventLoopGroup workerGroup = new NioEventLoopGroup();
    private static final int PORT = 8389;
    
    // 设置数据包大小限制为10MB
    private static final int MAX_CONTENT_LENGTH = 10 * 1024 * 1024; // 10MB

    // UUID到Channel的映射关系
    private static final Map<String, ChannelHandlerContext> uuid2ctx = new ConcurrentHashMap<>();
    
    // Channel到UUID的反向映射
    private static final Map<ChannelHandlerContext, String> ctx2uuid = new ConcurrentHashMap<>();

    /**
     * 检查设备是否在线
     * 
     * @param uuid 设备UUID
     * @return 如果设备在线则返回true，否则返回false
     */
    public static boolean isOnline(String uuid) {
        ChannelHandlerContext ctx = uuid2ctx.get(uuid);
        // 如果找到了对应的连接上下文，并且该连接是活跃的，则认为设备在线
        return ctx != null && ctx.channel().isActive();
    }

    /**
     * 启动 WebSocket 服务器
     */
    public static void launch() {
        try {
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_REUSEADDR, true)
                    .handler(new LoggingHandler(LogLevel.INFO))
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) {
                            ChannelPipeline pipeline = ch.pipeline();
                            // HTTP 协议编解码器
                            pipeline.addLast(new HttpServerCodec());
                            // 支持大数据流
                            pipeline.addLast(new ChunkedWriteHandler());
                            // 聚合 HTTP 消息，设置最大内容长度为10MB
                            pipeline.addLast(new HttpObjectAggregator(MAX_CONTENT_LENGTH));
                            // 添加 IdleStateHandler 心跳机制，当连接空闲时间太长时，会触发 IdleStateEvent 事件
                            pipeline.addLast(new IdleStateHandler(8, 0, 0, TimeUnit.SECONDS));
                            // WebSocket 协议处理器，设置最大帧长度为10MB
                            pipeline.addLast(new WebSocketServerProtocolHandler("/", null, true, MAX_CONTENT_LENGTH));
                            // 自定义业务处理器，现在使用内部类
                            pipeline.addLast(new WebSocketFrameHandler());
                        }
                    });

            // 绑定端口
            ChannelFuture future = bootstrap.bind(new InetSocketAddress(PORT)).sync();
            logger.info("WebSocket 服务器启动成功，监听端口: {}", PORT);
            
            // 等待服务器关闭
            // future.channel().closeFuture().sync();
        } catch (Exception e) {
            logger.error("WebSocket 服务器启动失败", e);
        }
    }
    
    /**
     * 关闭 WebSocket 服务器
     */
    @PreDestroy
    public void stop() {
        bossGroup.shutdownGracefully();
        workerGroup.shutdownGracefully();
        logger.info("WebSocket 服务器已关闭");
    }

    public static void main(String[] args) {
        launch();
    }
    
    /**
     * WebSocket 消息处理器 - 作为内部类
     */
    private static class WebSocketFrameHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {
        private static final Logger logger = LoggerFactory.getLogger(WebSocketFrameHandler.class);

        @Override
        public void handlerAdded(ChannelHandlerContext ctx) {
            logger.info("客户端已连接: {}", ctx.channel().remoteAddress());
        }

        @Override
        protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame frame) {
            try {

                String text = frame.text();
                System.out.println(text);

                JSONObject message = JSON.parseObject(text);
                String uuid = message.getString("ping");
                if(uuid != null) {
                    // 存储双向映射关系
                    uuid2ctx.put(uuid, ctx);
                    ctx2uuid.put(ctx, uuid);

                    // 响应客户端
                    ctx.channel().writeAndFlush(new TextWebSocketFrame("pong"));
                    return;
                }

                String event = message.getString("event");
                if("capture".equals(event)){

                    String to = message.getString("to");

                    // 发送给手机
                    JSONObject msg = new JSONObject();
                    msg.put("event", event);
                    msg.put("from", ctx2uuid.get(ctx));
                    uuid2ctx.get(to).channel().writeAndFlush(new TextWebSocketFrame(msg.toJSONString()));
                    return;
                }

                if("capture-response".equals(event)){

                    String to = message.getString("to");
                    String bitmapString = message.getString("bitmapString");

                    System.out.println("截图响应!!! to: " + to);

                    // 发送给手机
                    JSONObject msg = new JSONObject();
                    msg.put("event", event);
                    msg.put("from", ctx2uuid.get(ctx));
                    msg.put("bitmapString", bitmapString);
                    uuid2ctx.get(to).channel().writeAndFlush(new TextWebSocketFrame(msg.toJSONString()));
                    return;
                }

                if("debug".equals(event)){

                    String to = message.getString("to");
                    String script = message.getString("script");

                    // 发送给手机
                    JSONObject msg = new JSONObject();
                    msg.put("event", event);
                    msg.put("from", ctx2uuid.get(ctx));
                    msg.put("script", script);
                    uuid2ctx.get(to).channel().writeAndFlush(new TextWebSocketFrame(msg.toJSONString()));
                    return;
                }

                if("stop".equals(event)){

                    String to = message.getString("to");

                    // 发送给手机
                    JSONObject msg = new JSONObject();
                    msg.put("event", event);
                    msg.put("from", ctx2uuid.get(ctx));
                    uuid2ctx.get(to).channel().writeAndFlush(new TextWebSocketFrame(msg.toJSONString()));
                    return;
                }


                if("swipe".equals(event)){
                    String to = message.getString("to");
                    int x1 = message.getInteger("x1");
                    int y1 = message.getInteger("y1");
                    int x2 = message.getInteger("x2");
                    int y2 = message.getInteger("y2");
                    long duration = message.getLong("duration");

                    // 发送给手机
                    JSONObject msg = new JSONObject();
                    msg.put("event", event);
                    msg.put("x1", x1);
                    msg.put("y1", y1);
                    msg.put("x2", x2);
                    msg.put("y2", y2);
                    msg.put("duration", duration);
                    uuid2ctx.get(to).channel().writeAndFlush(new TextWebSocketFrame(msg.toJSONString()));
                    return;
                }

                if("back".equals(event)){
                    String to = message.getString("to");

                    // 发送给手机
                    JSONObject msg = new JSONObject();
                    msg.put("event", event);
                    uuid2ctx.get(to).channel().writeAndFlush(new TextWebSocketFrame(msg.toJSONString()));
                    return;
                }

                if("home".equals(event)){
                    String to = message.getString("to");

                    // 发送给手机
                    JSONObject msg = new JSONObject();
                    msg.put("event", event);
                    uuid2ctx.get(to).channel().writeAndFlush(new TextWebSocketFrame(msg.toJSONString()));
                    return;
                }

                if("recents".equals(event)){
                    String to = message.getString("to");

                    // 发送给手机
                    JSONObject msg = new JSONObject();
                    msg.put("event", event);
                    uuid2ctx.get(to).channel().writeAndFlush(new TextWebSocketFrame(msg.toJSONString()));
                    return;
                }

                if("request-log".equals(event)){
                    String to = message.getString("to");

                    // 发送给手机
                    JSONObject msg = new JSONObject();
                    msg.put("event", event);
                    msg.put("from", ctx2uuid.get(ctx));
                    uuid2ctx.get(to).channel().writeAndFlush(new TextWebSocketFrame(msg.toJSONString()));
                    return;
                }

                if("log".equals(event)){
                    String to = message.getString("to");
                    String content = message.getString("content");

                    // 发送给PC
                    JSONObject msg = new JSONObject();
                    msg.put("event", event);
                    msg.put("from", ctx2uuid.get(ctx));
                    msg.put("content", content);
                    uuid2ctx.get(to).channel().writeAndFlush(new TextWebSocketFrame(msg.toJSONString()));
                    return;
                }

            } catch (Exception e) {

            }
        }

        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
            ctx.close();
            cause.printStackTrace();
        }

        @Override
        public void handlerRemoved(ChannelHandlerContext ctx) {
            logger.info("客户端已断开: {}", ctx.channel().remoteAddress());
        }

        @Override
        public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
            if (evt instanceof IdleStateEvent) {
                IdleStateEvent event = (IdleStateEvent) evt;
                if (event.state() == IdleState.WRITER_IDLE) {
                    logger.info("WRITER_IDLE");
                } else
                if (event.state() == IdleState.READER_IDLE) {
                    logger.info("READER_IDLE");
                    ctx.close();
                }
            }
        }
    }
} 